{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "5fb7798b-4c96-44e7-9822-6da31cd9ceba",
   "metadata": {},
   "source": [
    "# Styling Options\n",
    "\n",
    ":::{note}\n",
    "These are the styling options shared by all the supported plotting backends. Backend-specific options can be found on the reference page of each plotting method (e.g. {meth}`hvplot.hvPlot.scatter`) or by executing `hvplot.help('scatter', docstring=False, generic=False, style=True)`.\n",
    ":::\n",
    "\n",
    "Visual styling options to adjust colors, fonts, and other aesthetic elements of the plot:\n",
    "\n",
    "```{eval-rst}\n",
    ".. plotting-options-table:: Styling Options\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "129d145e-956f-4435-9e7a-9033e2ebe93b",
   "metadata": {},
   "source": [
    "(option-backend_opts)=\n",
    "## `backend_opts`\n",
    "\n",
    "hvPlot offers many options to customize a plot. Yet, sometimes this is not enough, in which case `backend_opts` offers a powerful mechanism to customize many properties of the plot objects created internally by HoloViews (Bokeh models, Matplotlib figure and axis, etc.). This option accepts a dictionary whose keys are string accessors, i.e. they mirror attribute access on these plot objects. The values of this dictionary are the values to set.\n",
    "\n",
    "The objects that can be targeted via this accessor pattern are those referenced in the HoloViews plot `handles` dictionary. Usually that would be the `plot` key for Bokeh giving access to the [`figure`](https://docs.bokeh.org/en/latest/docs/reference/plotting/figure.html) object, and the `fig` key for Matplotlib giving access to the [`Figure`](https://matplotlib.org/stable/api/_as_gen/matplotlib.figure.Figure) object. Note that accessors starting with `plot` for Bokeh and `fig` for Matplotlib can omit these terms (e.g. `xgrid.grid_line_color` instead of `plot.xgrid.grid_line_colore` for Bokeh).\n",
    "\n",
    "Let's start with a Bokeh example where we set properties of the grid, the x- and y-axis, and hover. These options cannot be directly customized with the current hvPlot's API."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2aa1b2a7-aa24-40c0-9d84-63c4fcc3a964",
   "metadata": {},
   "outputs": [],
   "source": [
    "import hvplot.pandas  # noqa\n",
    "\n",
    "df = hvplot.sampledata.penguins(\"pandas\")\n",
    "\n",
    "plot = df.hvplot.scatter(\n",
    "    x=\"bill_length_mm\", y=\"bill_depth_mm\", color=\"black\",\n",
    "    backend_opts={\n",
    "        \"plot.xgrid.grid_line_color\": \"grey\",\n",
    "        \"plot.xgrid.grid_line_dash\": \"dotted\",\n",
    "        \"plot.xaxis.axis_label_text_font_style\": \"bold\",\n",
    "        \"plot.yaxis.axis_label_text_font_style\": \"bold\",\n",
    "        \"hover.attachment\": \"vertical\",\n",
    "    }\n",
    ")\n",
    "plot"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eee8f810-7d65-4c91-9f73-adc82097fcd5",
   "metadata": {},
   "source": [
    "With the code below, we can see all the handles we could have referenced for *this plot* (other plots may have different handles)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b06789f5-d78c-4e2a-9052-31845df33a67",
   "metadata": {},
   "outputs": [],
   "source": [
    "import holoviews as hv\n",
    "\n",
    "hv.renderer('bokeh').get_plot(plot).handles"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d080b507-b96d-419c-81a7-a448559207e8",
   "metadata": {},
   "source": [
    "We can see the Bokeh property `grid_line_color` exists and has been correctly set."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "742cc487-4a4a-4fea-aa53-f5fd8dc9ab36",
   "metadata": {},
   "outputs": [],
   "source": [
    "hv.renderer('bokeh').get_plot(plot).handles['plot'].xgrid.grid_line_color"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "297c4bba-85e2-40d7-b018-c7410cc1fb49",
   "metadata": {},
   "source": [
    "In this Matplotlib example we disable the plot frame. Note that we could also have written the accessor as `'axes.set_frame_on'` (which is the method called internally by HoloViews)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a0e3e933-b307-4943-b970-5f35860a9205",
   "metadata": {},
   "outputs": [],
   "source": [
    "import hvplot.pandas  # noqa\n",
    "hv.extension(\"matplotlib\")\n",
    "\n",
    "df = hvplot.sampledata.penguins(\"pandas\")\n",
    "\n",
    "plot = df.hvplot.scatter(\n",
    "    x=\"bill_length_mm\", y=\"bill_depth_mm\",\n",
    "    backend_opts={\"axes.frame_on\": False}\n",
    ")\n",
    "plot"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eb98d466-79dd-4c52-889b-7e66e0451e98",
   "metadata": {},
   "source": [
    "Here's the list of handles for *this plot*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "061b2b5b-b889-4c12-83d8-f46d3a7fe8fb",
   "metadata": {},
   "outputs": [],
   "source": [
    "hv.renderer('matplotlib').get_plot(plot).handles"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1bf917bb-5a8b-44d3-b55f-dbb09d00d4a7",
   "metadata": {},
   "source": [
    "We can see the Matplotlib method `set_frame_on` exists and has been correctly executed."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "756590f9-75ef-4acf-a238-b289fe142a83",
   "metadata": {},
   "outputs": [],
   "source": [
    "hv.renderer('matplotlib').get_plot(plot).handles['axis'].set_frame_on"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c9056f16-18b5-4e66-b70d-6180341d5b72",
   "metadata": {},
   "outputs": [],
   "source": [
    "hv.renderer('matplotlib').get_plot(plot).handles['axis'].get_frame_on()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "adfa012f-a28c-4487-ad55-26fea7f38aa9",
   "metadata": {},
   "source": [
    ":::{seealso}\n",
    "The [`legend_opts`](option-legend_opts) option to customize specifically the styling of the legend.\n",
    ":::"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "881751c3-4dc2-4027-9b7f-147fbe64a4f6",
   "metadata": {},
   "source": [
    "(option-fontscale)=\n",
    "## `fontscale`\n",
    "\n",
    "The `fontscale` option scales all the fonts in the plot by the provided numeric factor. For example, setting `fontscale=1.5` enlarges the title, tick labels, and axis labels by 50%. This is useful when you want to emphasize text for presentations or detailed viewing."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c1ddd044-1771-43a3-a09d-580749371644",
   "metadata": {},
   "outputs": [],
   "source": [
    "import hvplot.pandas  # noqa\n",
    "\n",
    "df = hvplot.sampledata.penguins(\"pandas\")\n",
    "\n",
    "df.hvplot.scatter(\n",
    "    x='bill_length_mm', y='bill_depth_mm', by='species',\n",
    "    fontscale=1.5, title=\"Penguins Species\",\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ee479a25-5270-4b60-91c3-5da24ae2a501",
   "metadata": {},
   "source": [
    "(option-fontsize)=\n",
    "## `fontsize`\n",
    "\n",
    "The `fontsize` option sets the font size for different text elements in the plot. It can be:\n",
    "\n",
    "- A single value (e.g. `12`) to apply to all text elements.\n",
    "- A dictionary to control specific elements like the title, axis labels, and ticks.  \n",
    "  Example: `{'title': '15pt', 'xlabel': '12pt', 'ylabel': '12pt', 'ticks': 10}`\n",
    "\n",
    "This option is useful for precise text control in reports, presentations, and dashboards.\n",
    "\n",
    "::: {note}\n",
    "**Backend-specific behavior**\n",
    "\n",
    "- The **Bokeh** backend supports both numeric values and strings with units like `'12pt'` or `'10px'`.\n",
    "- The **Matplotlib** backend only accepts **numeric values**. Using strings like `'14pt'` will not work.\n",
    "\n",
    "To ensure compatibility across backends, prefer using numbers unless you're specifically targeting Bokeh.\n",
    ":::"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1a2b11d7-91af-4600-aad1-910c85278b22",
   "metadata": {},
   "source": [
    "**Bokeh backend**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cb067733-9f58-42a6-9588-ca7b230407d3",
   "metadata": {},
   "outputs": [],
   "source": [
    "import hvplot.pandas  # noqa\n",
    "\n",
    "df = hvplot.sampledata.penguins(\"pandas\")\n",
    "\n",
    "df.hvplot.scatter(\n",
    "    x='bill_length_mm', y='bill_depth_mm', by='species',\n",
    "    fontsize={'title': '16pt', 'xlabel': '12pt', 'ylabel': '14pt', 'ticks': 10},\n",
    "    title=\"Penguins Measurements by Species\"\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3d50c50e-bf28-427f-8f7f-63514ead3a1c",
   "metadata": {},
   "source": [
    "**Matplotlib backend**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "408becf8-7758-4386-ae1f-b4daa8a80595",
   "metadata": {},
   "outputs": [],
   "source": [
    "import hvplot.pandas  # noqa\n",
    "hvplot.extension('matplotlib')\n",
    "\n",
    "df = hvplot.sampledata.penguins(\"pandas\")\n",
    "\n",
    "df.hvplot.scatter(\n",
    "    x='bill_length_mm', y='bill_depth_mm', by='species',\n",
    "    fontsize={'title': 22, 'xlabel': 16, 'ylabel': 18, 'ticks': 12},\n",
    "    title=\"Penguins Measurements by Species\"\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "20b26423",
   "metadata": {},
   "source": [
    "(option-grid)=\n",
    "## `grid`\n",
    "\n",
    "Turns grid lines on or off behind your data. By default grids are disabled; use `grid=True` to draw horizontal and vertical lines at each major tick mark."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "633c39bd",
   "metadata": {},
   "outputs": [],
   "source": [
    "import hvplot.pandas  # noqa\n",
    "\n",
    "df = hvplot.sampledata.stocks(\"pandas\", engine_kwargs={\"index_col\" : \"date\"})\n",
    "\n",
    "plot1 = df.hvplot(group_label=\"Company\", width=400, title=\"Default: grid=False\")\n",
    "plot2 = df.hvplot(group_label=\"Company\", grid=True, width=400, title=\"grid=True\")\n",
    "(plot1 + plot2).cols(1)"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
